/**
 * 
 */
package com.effectivsocial.service.api;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import javax.annotation.PostConstruct;


import jodd.bean.BeanUtil;

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;


import com.effectivsocial.domain.PersistentObject;
import com.effectivsocial.domain.User;




import com.effectivsocial.security.SecurityUtil;

/**
 * @author Dhrubo
 * 
 */
@Slf4j
@Transactional(readOnly = true)
public abstract class AbstractService<T, ID extends Serializable> implements
		BusinessService<T, ID> {

	private final String DELETED = "deleted";
	private final String CREATED_DATE = "createdDate";
	private final String LAST_MODIFIED_DATE = "lastModifiedDate";
	private final String CREATED_BY = "createdBy";
	private final String LAST_MODIFIED_BY = "lastModifiedBy";
	private final String VERSION = "version";
	private final String ID = "id";

	@Getter
	@Setter
	private MongoRepository<T, ID> repository;

	

	@Transactional(propagation = Propagation.REQUIRED)
	public T save(T t) {
		includeAuditForSave(t);
		return repository.save(t);
	}

	

	@Transactional(propagation = Propagation.REQUIRED)
	public void save(List<T> entities) {
		for (T t : entities) {
			if (t instanceof PersistentObject) {
				includeAuditForSave(t);
			}
		}
		repository.save(entities);
	}

	public T findOne(ID id) {
		return repository.findOne(id);
	}

	@Transactional(propagation = Propagation.REQUIRED)
	public void delete(T t) {
		BeanUtil.setPropertySilent(t, DELETED, true);
		update(t);
	}

	@Transactional(propagation = Propagation.REQUIRED)
	public void delete(List<T> entities) {
		for (T entity : entities) {
			delete(entity);
		}
	}

	@Transactional(propagation = Propagation.REQUIRED)
	public void purge(List<T> entities) {

		repository.delete(entities);
	}

	
	public void update(T source) {
		
		includeAuditForUpdate(source);
		repository.save(source);
	}

	/**
	 * Some useful methods
	 */
	protected void includeAuditForSave(Object bean) {
		Date timestamp = new Date();

		BeanUtil.setPropertySilent(bean, CREATED_DATE, timestamp);
		BeanUtil.setPropertySilent(bean, LAST_MODIFIED_DATE, timestamp);

		User user = SecurityUtil.getUserDetails();

		BeanUtil.setPropertySilent(bean, CREATED_BY, user);
		BeanUtil.setPropertySilent(bean, LAST_MODIFIED_BY, user);
	}

	protected void includeAuditForUpdate(Object bean) {
		Date timestamp = new Date();
		BeanUtil.setPropertySilent(bean, LAST_MODIFIED_DATE, timestamp);

		User user = SecurityUtil.getUserDetails();
		BeanUtil.setPropertySilent(bean, LAST_MODIFIED_BY, user);
	}

	/**
	 * This is a special method only used for setting 2 properties while
	 * creating an user
	 * 
	 * @param bean
	 */
	protected void includeAuditTimestamp(Object bean) {
		Date timestamp = new Date();
		BeanUtil.setPropertySilent(bean, CREATED_DATE, timestamp);
		BeanUtil.setPropertySilent(bean, LAST_MODIFIED_DATE, timestamp);
	}

	@PostConstruct
	public void init() {
		initInternal();
	}

	protected abstract void initInternal();

	@Override
	public long count() {
		return repository.count();
	} 

	@Override
	@Transactional(propagation = Propagation.REQUIRED)
	public void restore(T entity) {
		T target = repository.findOne((ID) BeanUtil
				.getDeclaredPropertySilently(entity, ID));

		BeanUtil.setPropertySilent(target, DELETED, false);

		includeAuditForUpdate(target);

	}

}
